﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using NUnit.Framework;
using XcoAppSpaces.Core.Ports;

namespace test.XcoAppSpaces.Core.Ports
{
	[TestFixture]
	public class PortSetNonGenericTests
	{
		[Test]
		[ExpectedException(typeof(ArgumentException))]
		public void Post_with_invalid_type_throws_exception()
		{
			//Arrange
			var portSet = new PortSet(typeof(int), typeof(string), typeof(double));
			
			//Act
			portSet.PostUnknownType(DateTime.Now);
		}

		[Test]
		public void PortSet_initialized_with_types_works_correctly()
		{
			//Arrange
			var portSet = new PortSet(typeof (int), typeof (string), typeof (double));

			//Act
			portSet.PostUnknownType(1);
			portSet.PostUnknownType("test");
			portSet.PostUnknownType(1.23);

			//Assert
			var ports = portSet.Ports.ToArray();
			var p0 = (Port<int>)ports[0];
			var p1 = (Port<string>)ports[1];
			var p2 = (Port<double>)ports[2];
			Assert.That(p0.Receive().Result, Is.EqualTo(1));
			Assert.That(p1.Receive().Result, Is.EqualTo("test"));
			Assert.That(p2.Receive().Result, Is.EqualTo(1.23));
		}

		[Test]
		public void PortSet_initialized_with_ports_works_correctly()
		{
			//Arrange
			var p0 = new Port<int>();
			var p1 = new Port<string>();
			var p2 = new Port<double>();
			var portSet = new PortSet(p0,p1,p2);

			//Act
			portSet.PostUnknownType(1);
			portSet.PostUnknownType("test");
			portSet.PostUnknownType(1.23);

			//Assert
			Assert.That(p0.Receive().Result, Is.EqualTo(1));
			Assert.That(p1.Receive().Result, Is.EqualTo("test"));
			Assert.That(p2.Receive().Result, Is.EqualTo(1.23));
		}

		[Test]
		public void Receiver_receives_on_all_ports()
		{
			//Arrange
			var portSet = new PortSet(typeof(int), typeof(string), typeof(double));
			var receivedItems = new List<object>();
			
			//Act
			Action<object> receiver = _ => receivedItems.Add(_);
			portSet.RegisterUnknownTypeReceiver(receiver);
			portSet.PostUnknownType(1);
			SpinWait.SpinUntil(() => receivedItems.Count == 1, 500);
			portSet.PostUnknownType("test");
			SpinWait.SpinUntil(() => receivedItems.Count == 2, 500);
			portSet.PostUnknownType(1.23);
			SpinWait.SpinUntil(() => receivedItems.Count == 3, 500);
			
			//Assert
			Assert.That(receivedItems, Has.Count.EqualTo(3));
			Assert.That(receivedItems[0], Is.EqualTo(1));
			Assert.That(receivedItems[1], Is.EqualTo("test"));
			Assert.That(receivedItems[2], Is.EqualTo(1.23));
		}

		class BaseType {}
		class DerivedType : BaseType{}

		[Test]
		public void Derived_message_type_can_be_posted()
		{
			//Arrange
			var p0 = new Port<BaseType>();
			var p1 = new Port<string>();
			var p2 = new Port<double>();
			var portSet = new PortSet(p0, p1, p2);

			//Act
			portSet.PostUnknownType(new DerivedType());
			
			//Assert
			Assert.That(p0.ItemCount, Is.EqualTo(1));
		}
	}
}
